/* 
 * File:   ImageHistogramBasedOnProjectionCreator.cpp
 * Author: Guilherme Monteiro
 */

#include "../headers/ImageHistogramBasedOnProjectionCreator.h"

ImageHistogramBasedOnProjectionCreator::ImageHistogramBasedOnProjectionCreator(IplImage* img) {
    this->originalImage = img;
}

ImageHistogramBasedOnProjectionCreator::ImageHistogramBasedOnProjectionCreator(const ImageHistogramBasedOnProjectionCreator& orig) {
}

ImageHistogramBasedOnProjectionCreator::~ImageHistogramBasedOnProjectionCreator() {
}

vector<double> ImageHistogramBasedOnProjectionCreator::calculatesVerticalProjection() {
    double sum;
    vector<double> vertical;
    //IplImage* image2 = cvCloneImage(image);
    
    for(int i = 0; i < originalImage->height; i++) {
        sum = 0;
        uchar* ptr = (uchar*)(originalImage->imageData + i*originalImage->widthStep); // line of pixels
        //uchar* ptr2 = (uchar*)(image2->imageData + i*image2->widthStep); // line of pixels
        for(int j = 0; j < originalImage->width; j++) {
            sum += ((ptr[3*j+1] + ptr[3*j] + ptr[3*j+2])/3); // pega os tres canais
        }
        sum /= originalImage->width; // average
//        for(int k = 0; k < image2->height; k++ ) { // pinta clone da imagem
//            if(sum < 30) {
//                ptr2[3*k] = 0;
//                ptr2[3*k+1] = 0;
//                ptr2[3*k+2] = 255;
//            }
//        }
        vertical.push_back(sum);
    }
    
//    for(int i = 0; i < vertical.size(); i++) {
//        printf("vertical projection: %.2f | %d\n", vertical.at(i), i);
//    }
//    printf("image->height: %d\n", originalImage->height);
    
    //cvNamedWindow("Image com linhas horizontais marcadas", CV_WINDOW_FREERATIO);
    //cvShowImage("Image com linhas horizontais marcadas", image2);
    //cvWaitKey(0);
    //cvReleaseImage(&image2);
    //cvDestroyWindow("Image com linhas horizontais marcadas");
    
    for(int i =0 ; i < originalImage->height; i++) {
        this->verticalProjection.push_back((int)vertical.at(i));
    }
    
    return vertical;
}

vector<double> ImageHistogramBasedOnProjectionCreator::calculatesHorizontalProjection() {
    vector<double> horizontal(originalImage->width);
    
    for(int i = 0; i < originalImage->width; i++) { // zera array
        horizontal[i] = 0;
    }
    
//    IplImage* image2 = cvCloneImage(image);
    double aux;
    for(int i = 0; i < originalImage->height; i++) {
        uchar* ptr = (uchar*)(originalImage->imageData + i*originalImage->widthStep); // line of pixels
        for(int j = 0; j < originalImage->width; j++) {
            aux = horizontal[j];
            horizontal[j] = ( aux + (ptr[3*j+1] + ptr[3*j+2] + ptr[3*j])/3 ); // all the channels
        }
    }    
    
    for(int m = 0; m < originalImage->width; m++) {
        horizontal[m] /= originalImage->height;
//        printf("projecao horizontal: %.2f | %d\n", horizontal[m], m);
    }
    
//    printf("image->width: %d\n", originalImage->width);
    
//    for(int k = 0; k < image2->height; k++) { // pinta clone da imagem
//        uchar* ptr2 = (uchar*)(image2->imageData + k*image2->widthStep); // line of pixels
//        for(int l = 0; l < image2->width; l++) {    
//            if(horizontal[l] < 90) {
//                ptr2[3*l] = 0;
//                ptr2[3*l+1] = 255;
//                ptr2[3*l+2] = 0;
//            }
//        }
//    }
        
//    cvNamedWindow("Image com linhas horizontais marcadas", CV_WINDOW_FREERATIO);
//    cvShowImage("Image com linhas horizontais marcadas", image2);
//    cvWaitKey(0);
//    cvReleaseImage(&image2);
//    cvDestroyWindow("Image com linhas horizontais marcadas");
    
//    printf("nchannels: %d\n", getImage()->nChannels);
    
    for(int i = 0 ; i < originalImage->width; i++) {
        this->horizontalProjection.push_back((int)horizontal[i]);
    }
    
    return horizontal;
}

/**
 * Create the image with the histogram
 * based on projection of lines and columns.
 * 
 * @param verticalP: vertical projection
 * @param horizontalP: horizontal projection
 * @return image with histogram
 */
IplImage* ImageHistogramBasedOnProjectionCreator::createImageWithHistogram(vector<double> verticalP, vector<double> horizontalP) {
    CvSize size;
    size.height = originalImage->height * 1.1;
    size.width = originalImage->width * 1.1;
    IplImage* newImage = cvCreateImage(size, IPL_DEPTH_8U, 3 );
    
    for(int i = 0; i < newImage->height; i++) { // puts white in full image
        uchar* ptr2 = (uchar*)(newImage->imageData + i*newImage->widthStep); // line of pixels
        for(int j = 0; j < newImage->width; j++) {
            ptr2[3*j] = 255;
            ptr2[3*j + 1] = 255;
            ptr2[3*j + 2] = 255;
        }
    }
    
    for(int i = 0; i < originalImage->height; i++) { // copy image to new image with histogram
        uchar* ptr = (uchar*)(originalImage->imageData + i*originalImage->widthStep); // line of pixels
        uchar* ptr2 = (uchar*)(newImage->imageData + i*newImage->widthStep); // line of pixels
        for(int j = 0; j < originalImage->width; j++) {
            ptr2[3*j] = ptr[3*j];
            ptr2[3*j + 1] = ptr[3*j + 1];
            ptr2[3*j + 2] = ptr[3*j + 2];
        }
    }
    
    newImage = fillHistogramWithVerticalProjection(newImage, verticalP);
    newImage = fillHistogramWithHorizontalProjection(newImage, horizontalP);
    
    return newImage;
}

/**
 * Preenche histograma com os 
 * dados da projecao vertical.
 * 
 * @param originalImg
 * @param imgHist
 * @param vProjection
 * @return image with histogram
 */
IplImage* ImageHistogramBasedOnProjectionCreator::fillHistogramWithVerticalProjection(IplImage* imgHist, vector<double> vProjection) {
    for(int i = 0; i < originalImage->height; i++) { 
        uchar* ptr = (uchar*)(imgHist->imageData + i*imgHist->widthStep); // line of pixels
        double normalizedValue = vProjection.at(i) * 0.1 * originalImage->width/255;
        for(int j = originalImage->width; j < (originalImage->width+normalizedValue); j++) {
              ptr[3*j] = 0; // BGR
              ptr[3*j + 1] = 0;
              ptr[3*j + 2] = 255;
        }
    }
    return imgHist;
}

/**
 * Preenche o histograma com os
 * dados da projecao horizontal.
 * 
 * @param originalImg
 * @param imgHist
 * @param hProjection
 * @return image with histogram
 */
IplImage* ImageHistogramBasedOnProjectionCreator::fillHistogramWithHorizontalProjection(IplImage* imgHist, vector<double> hProjection) {
    for(int i = originalImage->height; i < imgHist->height; i++) { 
        uchar* ptr = (uchar*)(imgHist->imageData + i*imgHist->widthStep); // line of pixels
        for(int j = 0; j < originalImage->width; j++) {
            double normalizedValue = hProjection[j] * 0.1 * originalImage->height/255;
            if(normalizedValue > abs(i-originalImage->height))  {
                ptr[3*j] = 0; // BGR
                ptr[3*j + 1] = 0;
                ptr[3*j + 2] = 255;
            }
        }
    }
    return imgHist;    
}

void ImageHistogramBasedOnProjectionCreator::run() {
    vector<double> verticalProjection;
    vector<double> horizontalProjection;
     
    verticalProjection = calculatesVerticalProjection();
    horizontalProjection = calculatesHorizontalProjection();
    
//    IplImage* imageWithHistogram = createImageWithHistogram(verticalProjection, horizontalProjection);
    
//    cvNamedWindow( "Image with histogram", CV_WINDOW_FREERATIO );
//    cvShowImage( "Image with histogram", imageWithHistogram );
//    cvWaitKey(0);
    
//    cvSaveImage("../logImages/imageWithHistogram.jpg", imageWithHistogram );
    
//    cvReleaseImage( &imageWithHistogram );
//    cvDestroyWindow( "Image with histogram" );
}

IplImage* ImageHistogramBasedOnProjectionCreator::getImage() {
    return this->originalImage;
}

vector<int> ImageHistogramBasedOnProjectionCreator::getVerticalProjection() {
    return this->verticalProjection;
}

vector<int> ImageHistogramBasedOnProjectionCreator::getHorizontalProjection() {
    return this->horizontalProjection;
}
